<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <input type="file" id="fileInput">
    <button id="file2DataUrlBtn">file2DataUrl</button>
    <button id="file2ImageBtn">file2Image</button>
    <button id="url2ImageBtn">url2Image</button>
    <button id="image2CanvasBtn">image2Canvas</button>
    <button id="canvas2DataUrlBtn">canvas2DataUrl</button>
    <button id="clearBtn">清空</button>
    <script>
        let fileInput = document.getElementById('fileInput');
        let clearBtn = document.getElementById('clearBtn');
        let file2DataUrlBtn = document.getElementById('file2DataUrlBtn');

        class UploadFile {
            constructor(dom) {
                this.dom = dom;
                this.file = null;
            }
            listenFileChange() {
                let _this = this;
                _this.dom.onchange = function (v) {
                    _this.file = v.target.files[0];
                    console.log(_this.file)
                    return Promise.resolve(_this.file)
                }
            };
            cleanFile() {
                this.file = null;
                this.dom.value = null;
            };
            /*
                转化为 data URL 字符串形式  
                Data URL 由四个部分组成：前缀（data:）、指示数据类型的 MIME 类型、如果非文本则为可选的 base64 标记、数据本身
                data:<mediatype>,<data>
                比如一张 png 格式图片，转化为 base64 字符串形式：
            */
            file2DataUrl(file, callback) {
                var reader = new FileReader();
                reader.onload = function () {
                    callback(reader.result);
                };
                reader.readAsDataURL(file);
            };
            /*
                若想将用户通过本地上传的图片放入缓存并 img 标签显示出来，
                除了可以利用以上方法转化成的 base64 字符串作为图片 src，还可以直接用 URL 对象，引用保存在 File 和 Blob 中数据的 URL。
                使用对象 URL 的好处是可以不必把文件内容读取到 JavaScript 中 而直接使用文件内容。为此，只要在需要文件内容的地方提供对象 URL 即可
                获取图片，本地预览显示
                要创建对象 URL，可以使用 window.URL.createObjectURL() 方法，并传入 File 或 Blob 对象。如果不再需要相应数据，最好释放它占用的内容。
                但只要有代码在引用对象 URL，内存就不会释放。要手工释放内存
            */
            file2Image(file, callback) {
                let _this = this;
                var image = new Image();
                var URL = window.webkitURL || window.URL;
                if (URL) {
                    var url = URL.createObjectURL(file);
                    image.onload = function () {
                        callback(image);
                        URL.revokeObjectURL(url);
                    };
                    image.src = url;
                } else {
                    _this.file2DataUrl(file, function (dataUrl) {
                        image.onload = function () {
                            callback(image);
                        }
                        image.src = dataUrl;
                    });
                }
            }
            // 通过图片链接（url）获取图片 Image 对象，由于图片加载是异步的，因此放到回调函数 callback 回传获取到的 Image 对象。
            url2Image(url, callback) {
                let image = new Image();
                image.src = url;
                image.onload = function () {
                    callback(image)
                }
            };

            /*
                利用 drawImage() 方法将 Image 对象绘画在 Canvas 对象上。
                drawImage 有三种语法形式：
                void ctx.drawImage(image, dx, dy);
                void ctx.drawImage(image, dx, dy, dWidth, dHeight);
                void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
                参数：
                    image 绘制到上下文的元素；
                    sx 绘制选择框左上角以 Image 为基准 X 轴坐标；
                    sy 绘制选择框左上角以 Image 为基准 Y 轴坐标；
                    sWidth 绘制选择框宽度；
                    sHeight 绘制选择框宽度；
                    dx Image 的左上角在目标 canvas 上 X 轴坐标；
                    dy Image 的左上角在目标 canvas 上 Y 轴坐标；
                    dWidth Image 在目标 canvas 上绘制的宽度；
                    dHeight Image 在目标 canvas 上绘制的高度；
            
            */
            image2Canvas(image) {
                var canvas = document.createElement('canvas');
                var ctx = canvas.getContext('2d');
                canvas.width = image.naturalWidth;
                canvas.height = image.naturalHeight;
                ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
                return canvas;
            }

            /*
                HTMLCanvasElement 对象有 toDataURL(type, encoderOptions) 方法，返回一个包含图片展示的 data URL 。同时可以指定输出格式和质量。
                参数分别为：
                type 图片格式，默认为 image/png。
                encoderOptions 在指定图片格式为 image/jpeg 或 image/webp 的情况下，可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围，将会使用默认值 0.92，其他参数会被忽略。
            */
            canvas2DataUrl(canvas, quality, type) {
                return canvas.toDataURL(type || 'image/jpeg', quality || 0.8);
            }


            // 图片链接也可以是 base64 字符串，直接赋值给 Image 对象 src 即可
            dataUrl2Image(dataUrl, callback) {
                var image = new Image();
                image.onload = function () {
                    callback(image);
                };
                image.src = dataUrl;
            }


            /*
                将 data URL 字符串转化为 Blob 对象。主要思路是：先将 data URL 数据（data） 部分提取出来，
                用 atob 对经过 base64 编码的字符串进行解码，再转化成 Unicode 编码，存储在Uint8Array（8位无符号整型数组，
                每个元素是一个字节） 类型数组，最终转化成 Blob 对象。
            */
            dataUrl2Blob(dataUrl, type) {
                var data = dataUrl.split(',')[1];
                var mimePattern = /^data:(.*?)(;base64)?,/;
                var mime = dataUrl.match(mimePattern)[1];
                var binStr = atob(data);
                var arr = new Uint8Array(len);

                for (var i = 0; i < len; i++) {
                    arr[i] = binStr.charCodeAt(i);
                }
                return new Blob([arr], { type: type || mime });
            }

            /*
                HTMLCanvasElement 有 toBlob(callback, [type], [encoderOptions]) 方法创造 Blob 对象，用以展示 canvas 上的图片；
                这个图片文件可以被缓存或保存到本地，由用户代理端自行决定。第二个参数指定图片格式，如不特别指明，图片的类型默认为 image/png，
                分辨率为 96dpi。第三个参数用于针对image/jpeg 格式的图片进行输出图片的质量设置。
                为兼容低版本浏览器，作为 toBlob 的 polyfill 方案，可以用上面 data URL 生成 Blob 方法 dataUrl2Blob 作为HTMLCanvasElement 原型方法。
            */
            canvas2Blob(canvas, callback, quality, type) {
                if (!HTMLCanvasElement.prototype.toBlob) {
                    Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
                        value: function (callback, type, quality) {
                            let dataUrl = this.toDataURL(type, quality);
                            callback(dataUrl2Blob(dataUrl));
                        }
                    });
                } else {
                    canvas.toBlob(function (blob) {
                        callback(blob);
                    }, type || 'image/jpeg', quality || 0.8);
                }
            }
        
            /*
                将 Blob 对象转化成 data URL 数据，由于 FileReader 的实例 readAsDataURL 方法不仅支持读取文件，
                还支持读取 Blob 对象数据，这里复用上面 file2DataUrl 方法即可：
            */ 
            blob2DataUrl(blob, callback){
                file2DataUrl(blob, callback);
            }

            // 将 Blob 对象转化成 Image 对象，可通过 URL 对象引用文件，也支持引用 Blob 这样的类文件对象，同样，这里复用上面 file2Image 方法即可：
            blob2Image(blob, callback){
                file2Image(blob, callback)
            }

            
        
        }

        let getFile = new UploadFile(fileInput);
        getFile.listenFileChange();

        file2DataUrlBtn.onclick = function () {
            let file = getFile.file;
            getFile.file2DataUrl(file, file => {
                console.log(file)
            })
        }

        file2ImageBtn.onclick = function () {
            let file = getFile.file;
            getFile.file2Image(file, img => {
                document.body.appendChild(img)
                setTimeout(() => {
                    document.body.removeChild(img);
                    clearBtn.click();
                }, 5000)
            })
        }

        url2ImageBtn.onclick = function () {
            getFile.url2Image('./timg.jpg', img => {
                console.log(img)
                document.body.appendChild(img)
                setTimeout(() => {
                    document.body.removeChild(img);
                    clearBtn.click();
                }, 5000)
            })
        }

        image2CanvasBtn.onclick = function () {
            let file = getFile.file;
            getFile.file2Image(file, img => {
                let canvas = getFile.image2Canvas(img);
                console.log(canvas)
                document.body.appendChild(canvas)
                setTimeout(() => {
                    document.body.removeChild(canvas);
                    clearBtn.click();
                }, 5000)
            })
        }

        clearBtn.onclick = function () {
            getFile.cleanFile();
        }
    </script>
</body>

</html>